#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <vector>

#include "Parser.h"

#include "CodeTimeing.h"

void CaptureTest(void);

void CompileFailTest(void);

void EscapeTest     (void);

void QuantiferTest  (void);
void TimeTest       (void);
void HTML           (void);
void WildAtFront    (void);
void OrTastic       (void);
void DeepSubExps    (void);
void NumberDeduction(void);

void Run(const char *data, const char **sets, const char **out, const char **ids, int nsets, int nout);
void TimeRun(const char *str, const char **sets, int nsets);

void TestRegExp(void)
{
    /* Run some test to check to see if bad RegExs are rejected. */
  CompileFailTest();

  //CaptureTest(); // Requires features that are currently not implemented
  //EscapeTest();  // Not automated. You have to manually look at the byte code.

    /* Perform a basic quantifer test with ? */
  QuantiferTest();

    /* Run some timed stress tests to make sure the growth rate is not        *
     *   exponential.                                                         */
  TimeTest();

    /* Run a somewhat real world example through the RegEx engine.            */
  HTML();

    /* Check to see if a wild card at the front is handled correctly.         */
  WildAtFront();

    /* Test a high level of | */
  OrTastic();

    /* Run some basic number deduction tests. IE, attempt to figure out what  *
     *   data type a string is holding.                                       */
  NumberDeduction();

  //DeepSubExps();  // Requires features that are currently not implemented
}


void CompileFailTest(void)
{
  unsigned int i;
  const char *sets[] = {"a(b", "a(b))", "ab)", "a[b", "ab]", "a[b]]"};

  ParserInit(0, 1024);
  ParserEnable(PARSER_REGULAR_EXPRESSIONS);

  for(i = 0; i < sizeof(sets) / sizeof(char *); ++i)
    if(!AddTokenSeparator(sets[i], 0, -1, 0, 0, 0))
      printf("Expression |%s| should not have compiled\n", sets[i]);


  ParserDisable(PARSER_REGULAR_EXPRESSIONS);

  for(i = 0; i < sizeof(sets) / sizeof(char *); ++i)
    AddTokenSeparator(sets[i], 0, -1, 0, 0, 0);

  ParserEnable(PARSER_REGULAR_EXPRESSIONS);

  ParserDeInit();
}

void CaptureTest(void)
{
  const char *data = "110001110101000101001011010010010010110110110010101000111010001010101011010111010010100";
  const char *sets[] = {"([01]*)([01]*)([01]*)([01]*)([01]*)([01]*)([01]*)([01]*)([01]*)([01]*)"};

  TimeRun(data, sets, sizeof(sets) / sizeof(char *));
}

void Template(void)
{
  const char *data   = "";
  const char *sets[] = {""};
  const char *out[]  = {""};
  const char *ids[]  = {""};

  printf("\n");
  Run(data, sets, out, ids, sizeof(sets) / sizeof(char *), sizeof(out) / sizeof(char *));
  printf("\n\n");
}

void EscapeTest(void)
{
  const char *data   = "";
  const char *sets[] = {"foo\\.\\\\d\\d\\w[1-3]\\n[235\\D4][]][^]...][-..]\\[..-]\\(\\)\\?\\.\\+\\|\\\\\\[[bar"};
  const char *out[]  = {""};
  const char *ids[]  = {""};

  printf("\n");
  Run(data, sets, out, ids, sizeof(sets) / sizeof(char *), sizeof(out) / sizeof(char *));
  printf("\n\n");
}

void QuantiferTest(void)
{
  const char *data   = "aaab";
  const char *sets[] = {"a?a?aaa"};
  const char *out[]  = {"aaa", "b"};
  const char *ids[]  = {""};

  printf("QuantifierTest\n");
  Run(data, sets, out, ids, sizeof(sets) / sizeof(char *), sizeof(out) / sizeof(char *));
  printf("\n\n");
}

#define DOUBLE_UP 0
#define TRIPLE_UP 1

void TimeTest(void)
{
  int i, j;
  char *data;
  char *sets[] = {0};

  printf("\n");
  for(i = 3; i <= 10; ++i)
  {
    #if TRIPLE_UP
      data = (char *)malloc(i * 3 + 1);
      for(j = 0; j < i * 3; ++j)
        data[j] = 'a';
      data[j] = 0;
    #elif DOUBLE_UP
      data = (char *)malloc(i * 2 + 1);
      for(j = 0; j < i * 2; ++j)
        data[j] = 'a';
      data[j] = 0;
    #else
      data = (char *)malloc(i + 1);
      for(j = 0; j < i; ++j)
        data[j] = 'a';
      data[j] = 0;
    #endif
    sets[0] = (char *)malloc((3 * i) + 1);
    for(j = 0; j < i; ++j)
    {
      sets[0][j * 2 + 0] = 'a';
      sets[0][j * 2 + 1] = '?';
      sets[0][2 * i + j] = 'a';
    }
    sets[0][2 * i + j] = 0;

    printf("Len: %4d: ", i);
    //SCodeTime();
    //Run(data, sets, 0, 0, sizeof(sets) / sizeof(char *), 0);
    //CodeTime();
    TimeRun(data, (const char **)sets, sizeof(sets) / sizeof(char *));
    printf("\n");

    free(data);
    free(sets[0]);
  }
  printf("\n\n");
}

void HTML(void)
{
  const char *data   = "One king <br foo=\"   \" > <mal\"\"> </mal foo=\"bar\"> <mal <mal2 \"> </br> Max Occ: 3 adults or 2 adults and 2 children 5-12 years old. ";
  const char *sets[] = {"<[a-zA-Z]+( *[a-zA-Z]+ *= *\"[^\"]+\" *| *[a-zA-Z]+ *)* */?>|</[a-zA-Z]+ *>"};

  const char *out[]  = {"One king ", "<br foo=\"   \" >", " <mal\"\"> </mal foo=\"bar\"> <mal <mal2 \"> ", "</br>", " Max Occ: 3 adults or 2 adults and 2 children 5-12 years old. "};
  const char *ids[]  = {""};

  printf("|%s|\n", sets[0]);
  printf("\n");
  Run(data, sets, out, ids, sizeof(sets) / sizeof(char *), sizeof(out) / sizeof(char *));
  printf("\n\n");
}

void WildAtFront(void)
{
  int i;
  const char *data   = "asdf foobar";
  const char *sets[] = {".*foo",
                  "(.*)oo",
                  "(.*).*oo",
                  //"(.*o){2}",
                  //"(.*o){1}.*o",
                  ".*o.?o",
                  //"(.){5}foo",
                  //".{10}foo|.*foo",
                  "((((.*)))(.*bad|foo))"

                    // Different outputs, but valid
                  //"(.?)oo",
                  //"foo.*(a|r)"
                  //"foo.*"

                  //"((((.*)))(.*bad|foo))(.*)r"
                 };
  const char *out[]  = {"asdf foo", "bar"};
  const char *ids[]  = {""};

  printf("Wild At Front\n");
  for(i = 0; i < sizeof(sets) / sizeof(char *); ++i)
  {
    printf("  |%s|\n", sets[i]);
    Run(data, sets + i, out, ids, 1, sizeof(out) / sizeof(char *));
  }
  //Run(data, sets, out, ids, sizeof(sets) / sizeof(char *), sizeof(out) / sizeof(char *));
  printf("\n\n");
}

void OrTastic(void)
{
  const char *data   = "abcd eFgHiJkK dddEffhg a83vd4 9j LBS3 3458FS!@#$%";
  const char *sets[] = {"(((((((((((a|A|b|B|c|C|d|D|e|E|f|F|g|G|h|H|i|I|j|J|k|K|l|L|m|M|n|N|o|O|p|P|q|Q|r|R|s|S|t|T|u|U|v|V|w|W|x|X|y|Y|z|Z)+|0)|1)|2)|3)|4)|5)|6)|7)|8)|9)+|(!([@#$%]*))"};
  const char *out[]  = {"abcd", " ", "eFgHiJkK", " ", "dddEffhg", " ", "a83vd4", " ", "9j", " ", "LBS3", " ", "3458FS", "!@#$%"};
  const char *ids[]  = {""};

  printf("Or Tastic\n");
  Run(data, sets, out, ids, sizeof(sets) / sizeof(char *), sizeof(out) / sizeof(char *));
  printf("\n\n");
}

void DeepSubExps(void)
{
  const char *data   = "bcdefghijklmno end";
  const char *sets[] = {"(a|[^a]{3}(ef(ghi(jkl){3,5}|ghijkl)mno)*)"};
  const char *out[]  = {"bcdefghijklmno", " en", "d"};
  const char *ids[]  = {"1"};

  printf("DeepSubExps\n");
  Run(data, sets, out, ids, sizeof(sets) / sizeof(char *), sizeof(out) / sizeof(char *));
  printf("\n\n");
}

void NumberDeduction(void)
{
  const char *data   = " 8.3 9.1f 53 09 07 0x01FF <foo bar=\"ddd\" bar car=\"dd\"> </foo> 0b11010111 0b012 dhj \ndhj dhj";
  const char *sets[] = {"\\b[0-9]+\\.[0-9]*f\\b", "\\b[0-9]+\\.[0-9]*\\b",
                  "\\b[1-9][0-9]*\\b",     "\\b0[0-7]+\\b",
                  "\\b0x[0-9A-F]+\\b",     "\\b0b[01]+\\b",
                  "</?[a-zA-Z]+( *[a-zA-Z]+ *= *\"[^\"]+\" *| *[a-zA-Z]+ *)* */?>",
                  "dhj$", "^dhj"};
  const char *out[]  = {" ", "8.3", " ", "9.1f", " ", "53", " 09 ", "07", " ", "0x01FF", " ", "<foo bar=\"ddd\" bar car=\"dd\">", " ", "</foo>", " ", "0b11010111", " 0b012 dhj \n", "dhj", " ", "dhj"};
  const char *ids[]  = {"Float", "Double", "Int", "Oct", "Hex", "Bin", "XML", "End", "Start"};

  printf("Number Deduction\n");
  Run(data, sets, out, ids, sizeof(sets) / sizeof(char *), sizeof(out) / sizeof(char *));
  printf("\n\n");
}

void Run(const char *str, const char **sets, const char **out, const char **ids, int nsets, int nout)
{
  int i;
  char *buffer;
  int o;
  int bufsize;

  FILE *fp = fopen("dgega.txt", "wb");
  fwrite(str, 1, strlen(str), fp);
  fclose(fp);

  for(bufsize = 0; bufsize <= (int)strlen(str) + 2; ++bufsize)
  {
    ParserInit(0, bufsize);
    //ParserInit(0, 1024);
    ParserEnable(PARSER_REGULAR_EXPRESSIONS);

    LoadFile("dgega.txt");

    //LoadMemory(str);

    for(i = 0; i < nsets; ++i)
      AddTokenSeparator(sets[i], 1, -1, 0, 0, 0);

    //ParserDeInit();
    //remove("dgega.txt");
    //break;

    //printf("  BufSize: %3d\n", bufsize);
    o = 0;
    for(buffer = GrabToken(); buffer; buffer = GrabToken())
    {
      //printf("    |%s|\n", buffer);
      if(nout && o >= nout)
        printf("  BufSize: %3d: Extra Token: |%s|\n", bufsize, buffer);
      else
      if(nout && strcmp(buffer, out[o++]))
        printf("  BufSize: %3d: |%s| != |%s|\n", bufsize, buffer, out[o - 1]);
      free(buffer);
    }

    while(o < nout)
      printf("  BufSize: %3d: Missing Token: |%s|\n", bufsize, out[o++]);
    //printf("\n");

    ParserDeInit();
    break;
  }

  remove("dgega.txt");
}

void TimeRun(const char *str, const char **sets, int nsets)
{
  char *buffer;
  int   i;
  int   bufsize;

  for(bufsize = 0; bufsize <= (int)strlen(str) + 2; ++bufsize)
  {
    ParserInit(0, bufsize);
    //ParserInit(0, 1024);
    ParserEnable(PARSER_REGULAR_EXPRESSIONS);

    LoadMemory(str);

    for(i = 0; i < nsets; ++i)
      AddTokenSeparator(sets[i], 0, -1, 0, 0, 0);

    SCodeTime();
    buffer = GrabToken();
    CodeTime();

    if(buffer)
      free(buffer);

    ParserDeInit();
    break;
  }

  remove("dgega.txt");
}
